
class AtrField:
    def __init__(self, start, size):
        self.start = start
        self.size = size

    def __repr__(self):
        return '<AtrField>: ' + self.dump()

    def __add__(self, obj):
        if obj is None:
            return self
        elif isinstance(obj, AtrField):
            # no strict check if the two fields are neighbours
            return AtrField(min(self.start, obj.start), self.size + obj.size)
        else:
            raise ab_exceptions.AofException('<AtrField>',
                                             'cannot recognize "%s"' %
                                             str(obj))

    def __eq__(self, obj):
        return isinstance(obj, AtrField) and \
            self.start == obj.start and self.size == obj.size

    def __ne__(self, obj):
        return not self.__eq__(obj)

    def getZone(self):
        return (self.start, self.size)

    def getStart(self):
        return self.start

    def getSize(self):
        return self.size

    def getEnd(self):
        return self.start + self.size

    def adjust(self, start=-1, size=-1):
        if start != -1:
            self.start += start

        if size != -1:
            self.size = size

    def inside(self, field):
        return self.start <= field.start and \
            field.start + field.size <= self.start + self.size

    def dump(self, json=False):
        if json:
            return (self.start, self.size)
        else:
            return 'Start(%d) Size(%d)' % (self.start, self.size)


class AtrFields:
    def __init__(self, fields=None):
        self.fields = list()
        if isinstance(fields, AtrField):
            self.append(fields)
        elif isinstance(fields, (list, tuple)):
            self.extend(fields)

    def __repr__(self):
        return '<AtrFields>: ' + self.dump()

    def __len__(self):
        return len(self.fields)

    def __eq__(self, obj):
        if isinstance(obj, AtrFields) and len(self.fields) == len(obj.fields):
            for k, fd in enumerate(self.fields):
                if fd != obj.fields[k]:
                    break
            else:
                return True

        return False

    def __ne__(self, obj):
        return not self.__eq__(obj)

    def __getitem__(self, key):
        if key < len(self.fields):
            return self.fields[key]
        else:
            return None

    def __setitem__(self, key, value):
        if key < len(self.fields):
            self.fields[key] = value

    def append(self, field, joined=True):
        if isinstance(field, AtrField):
            for k, fd in enumerate(self.fields):
                if fd.getStart() > field.getStart():
                    self.fields.insert(k, field)
                    break
            else:
                self.fields.append(field)

            if joined:
                self.refresh()

            return True

        return False

    def extend(self, fields, joined=True):
        for fd in fields:
            self.append(fd, joined)

    def refresh(self):
        merged = True
        size = len(self.fields)

        while merged:
            merged = False
            num = len(self.fields) - 1

            while num > 1:
                fa = self.fields[num]
                fp = self.fields[num - 1]
                if fp.getStart() < fa.getStart() and \
                        fa.getStart() <= fp.getEnd() <= fa.getEnd():
                    fp.adjust(size=fa.getEnd() - fp.getStart())

                    del self.fields[num]
                    merged = True
                elif fp.getStart() < fa.getStart() and \
                        fp.getEnd() > fa.getEnd():
                    del self.fields[num]
                    merged = True

                if merged:
                    break

                num -= 1

        return len(self.fields) != size

    def inside(self, field):
        for fd in self.fields:
            if fd.inside(field):
                return True

        return False

    def adjust(self, start=-1, size=-1):
        for fd in self.fields:
            fd.adjust(start, size)

    def getFields(self):
        return self.fields[:]

    def getStart(self):
        if len(self.fields) > 0:
            return self.fields[0].getStart()
        else:
            return 0

    def getSize(self):
        size = 0

        for fd in self.fields:
            size += fd.getSize()

        return size

    def json(self):
        ret = list()

        for fd in self.fields:
            ret.append(fd.dump(json=True))

        return ret

    def dump(self, json=False):
        if json:
            return self.json()

        ret = ''
        for fd in self.fields:
            ret += '(%s)' % fd.dump()

        return ret
